#pragma once

#include "Package.h"

#include "NamedElement.h"
#include "PackageMerge.h"
#include "PackageableElement.h"
#include "Profile.h"
#include "ProfileApplication.h"
#include "Stereotype.h"
#include "Type.h"

namespace uml {

const QString Package::URI_default_ = "";
Package::Package(const QString& umlType)
    : PackageableElement(umlType) {}

void Package::fromJson(const QJsonObject& json, QStringList& errorList) {
    if (json.isEmpty()) {
        return;
    }

    TemplateableElement::fromJson(json, errorList);
    Namespace::fromJson(json, errorList);
    PackageableElement::fromJson(json, errorList);
    URI_ = json.value("URI").toString(URI_default_);
    JsonSerializable::fromJsonArray(nestedPackages_, "nestedPackages", json, errorList);
    nestingPackage_ = JsonSerializable::create<Package>(json.value("nestingPackage"), errorList);

    JsonSerializable::fromJsonArray(ownedStereotypes_, "ownedStereotypes", json, errorList);
    JsonSerializable::fromJsonArray(ownedTypes_, "ownedTypes", json, errorList);
    JsonSerializable::fromJsonArray(packageMerges_, "packageMerges", json, errorList);
    JsonSerializable::fromJsonArray(packagedElements_, "packagedElements", json, errorList);
    JsonSerializable::fromJsonArray(profileApplications_, "profileApplications", json, errorList);
}

void Package::toJson(QJsonObject& json) {
    TemplateableElement::toJson(json);
    Namespace::toJson(json);
    PackageableElement::toJson(json);
    if (URI_ != URI_default_) {
        json.insert("URI", URI_);
    }
    JsonSerializable::toJsonArray(nestedPackages_, "nestedPackages", json);
    if (nestingPackage_) {
        QJsonObject nestingPackage_json;
        nestingPackage_->toJson(nestingPackage_json);
        json.insert("nestingPackage", nestingPackage_json);
    }
    JsonSerializable::toJsonArray(ownedStereotypes_, "ownedStereotypes", json);
    JsonSerializable::toJsonArray(ownedTypes_, "ownedTypes", json);
    JsonSerializable::toJsonArray(packageMerges_, "packageMerges", json);
    JsonSerializable::toJsonArray(packagedElements_, "packagedElements", json);
    JsonSerializable::toJsonArray(profileApplications_, "profileApplications", json);
}

/// <summary>
/// allApplicableStereotypes
    /// The query allApplicableStereotypes() returns all the directly or indirectly
    /// owned stereotypes, including stereotypes contained in sub-profiles.
/// </summary>
    /// <code>
    /// result = (let ownedPackages : Bag(Package) = ownedMember->select(oclIsKindOf(Package))->collect(oclAsType(Package)) in
    ///  ownedStereotype->union(ownedPackages.allApplicableStereotypes())->flatten()->asSet()
    /// )
    /// </code>
shared_ptr<vector<Stereotype>> Package::allApplicableStereotypes() const
{
    return nullptr;
}

/// <summary>
/// containingProfile
    /// The query containingProfile() returns the closest profile directly or indirectly
    /// containing this package (or this package itself, if it is a profile).
/// </summary>
    /// <code>
    /// result = (if self.oclIsKindOf(Profile) then 
    /// 	self.oclAsType(Profile)
    /// else
    /// 	self.namespace.oclAsType(Package).containingProfile()
    /// endif)
    /// </code>
shared_ptr<Profile> Package::containingProfile() const
{
    return nullptr;
}

/// <summary>
/// makesVisible
    /// The query makesVisible() defines whether a Package makes an element visible
    /// outside itself. Elements with no visibility and elements with public visibility
    /// are made visible.
/// </summary>
    /// <code>
    /// member->includes(el)
    /// </code>
    /// <code>
    /// result = (ownedMember->includes(el) or
    /// (elementImport->select(ei|ei.importedElement = VisibilityKind::public)->collect(importedElement.oclAsType(NamedElement))->includes(el)) or
    /// (packageImport->select(visibility = VisibilityKind::public)->collect(importedPackage.member->includes(el))->notEmpty()))
    /// </code>
bool Package::makesVisible(shared_ptr<NamedElement> el) const
{
    return false;
}

/// <summary>
/// mustBeOwned
    /// The query mustBeOwned() indicates whether elements of this type must have an
    /// owner.
/// </summary>
    /// <code>
    /// result = (false)
    /// </code>
bool Package::mustBeOwned() const
{
    return false;
}

/// <summary>
/// visibleMembers
    /// The query visibleMembers() defines which members of a Package can be accessed
    /// outside it.
/// </summary>
    /// <code>
    /// result = (member->select( m | m.oclIsKindOf(PackageableElement) and self.makesVisible(m))->collect(oclAsType(PackageableElement))->asSet())
    /// </code>
shared_ptr<vector<PackageableElement>> Package::visibleMembers() const
{
    return nullptr;
}

/// <summary>
/// URI getter
/// Provides an identifier for the package that can be used for many purposes. A URI
/// is the universally unique identification of the package following the IETF URI
/// specification, RFC 2396 http://www.ietf.org/rfc/rfc2396.txt and it must comply
/// with those syntax rules.
/// </summary>
QString Package::URI() const {
    return URI_;
}

/// <summary>
/// URI setter
/// Provides an identifier for the package that can be used for many purposes. A URI
/// is the universally unique identification of the package following the IETF URI
/// specification, RFC 2396 http://www.ietf.org/rfc/rfc2396.txt and it must comply
/// with those syntax rules.
/// </summary>
void Package::setURI(const QString& value) {
    URI_ = value;
}

/// <summary>
/// nestedPackages getter
/// References the packaged elements that are Packages.
/// </summary>
vector<shared_ptr<Package>>& Package::nestedPackages() {
    return nestedPackages_;
}

/// <summary>
/// nestingPackage getter
/// References the Package that owns this Package.
/// </summary>
shared_ptr<Package> Package::nestingPackage() const {
    return nestingPackage_;
}

/// <summary>
/// nestingPackage setter
/// References the Package that owns this Package.
/// </summary>
void Package::setNestingPackage(shared_ptr<Package> value) {
    nestingPackage_ = value;
}

/// <summary>
/// ownedStereotypes getter
/// References the Stereotypes that are owned by the Package.
/// </summary>
vector<shared_ptr<Stereotype>>& Package::ownedStereotypes() {
    return ownedStereotypes_;
}

/// <summary>
/// ownedTypes getter
/// References the packaged elements that are Types.
/// </summary>
vector<shared_ptr<Type>>& Package::ownedTypes() {
    return ownedTypes_;
}

/// <summary>
/// packageMerges getter
/// References the PackageMerges that are owned by this Package.
/// </summary>
vector<shared_ptr<PackageMerge>>& Package::packageMerges() {
    return packageMerges_;
}

/// <summary>
/// packagedElements getter
/// Specifies the packageable elements that are owned by this Package.
/// </summary>
vector<shared_ptr<PackageableElement>>& Package::packagedElements() {
    return packagedElements_;
}

/// <summary>
/// profileApplications getter
/// References the ProfileApplications that indicate which profiles have been
/// applied to the Package.
/// </summary>
vector<shared_ptr<ProfileApplication>>& Package::profileApplications() {
    return profileApplications_;
}

} // namespace uml
